Flutter问题记录 您所在的位置:网站首页 flutter image_picker限制数量 Flutter问题记录

Flutter问题记录

2023-06-05 05:08| 来源: 网络整理| 查看: 265

文章目录 前言开发环境问题描述问题分析解决方案最后

前言

梳理Flutter项目的过程中发现还有一些遗留的TODO没处理,其中有一个和Text组件相关。

开发环境 Flutter: 3.7.12Dart: 2.19.6 问题描述

Text组件设置maxLines: null不限制行数:

Text( 'The [Text] widget displays a string of text with single style. The string might break across multiple lines or might all be displayed on the same line depending on the layout constraints.', softWrap: true, overflow: TextOverflow.ellipsis, maxLines: null, )

设置无效,只显示一行:

screenshot1

当时赶项目进度,没时间细究,暂时通过设置maxLines: 9999实现虚假的不限制行数。

问题分析

先看看maxLines的文档注释:

/// An optional maximum number of lines for the text to span, wrapping if necessary. /// If the text exceeds the given number of lines, it will be truncated according /// to [overflow]. /// /// If this is 1, text will not wrap. Otherwise, text will be wrapped at the /// edge of the box. /// /// If this is null, but there is an ambient [DefaultTextStyle] that specifies /// an explicit number for its [DefaultTextStyle.maxLines], then the /// [DefaultTextStyle] value will take precedence. You can use a [RichText] /// widget directly to entirely override the [DefaultTextStyle].

从文档中可知,当maxLines设为null时,如果DefaultTextStyle中有指定maxLines的值,那么将以这个值为准。查看Text组件的build方法,确实是这样的。

Text的build方法(省略部分):

@override Widget build(BuildContext context) { final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); ... Widget result = RichText( ..., maxLines: maxLines ?? defaultTextStyle.maxLines, ..., ); ... return result; }

问题的原因难道就是这个?断点调试看看:

screenshot2

DefaultTextStyle并没有指定maxLines的值,也是null。通过不断断点调试,可以很确定maxLines的值一直都是null。这难道是Flutter的bug?想了想应该不可能,如果是Flutter的bug,这么明显的问题应该早就被修复了。

假定Flutter没问题,那问题只能出在Text组件的使用上。除了maxLines,还设置了softWrap和overflow属性。咦🤔?️不限制行数的情况下,文本不存在溢出情况,那设置overflow属性完全是多此一举。移除overflow: TextOverflow.ellipsis,重新运行一切正常!

TextOverflow定义:

enum TextOverflow { /// Clip the overflowing text to fix its container. clip, /// Fade the overflowing text to transparent. fade, /// Use an ellipsis to indicate that the text has overflowed. ellipsis, /// Render overflowing text outside of its container. visible, }

奇怪的是只有TextOverflow.ellipsis有问题,其他文本溢出处理方式没问题。

screenshot3

通过追踪overflow属性的传递,来到RenderParagraph类的构造方法:

screenshot4

初始化TextPainter时,如果overflow属性值等于TextOverflow.ellipsis,会用_kEllipsis常量初始化ellipsis。_kEllipsis常量定义:

// '\u2026'即省略号'…' const String _kEllipsis = '\u2026';

继续往下走,在TextPainter类中找到关于ellipsis的定义:

/// The string used to ellipsize overflowing text. Setting this to a non-empty /// string will cause this string to be substituted for the remaining text /// if the text can not fit within the specified maximum width. /// /// Specifically, the ellipsis is applied to the last line before the line /// truncated by [maxLines], if [maxLines] is non-null and that line overflows /// the width constraint, or to the first line that is wider than the width /// constraint, if [maxLines] is null. The width constraint is the `maxWidth` /// passed to [layout]. /// /// After this is set, you must call [layout] before the next call to [paint]. /// /// The higher layers of the system, such as the [Text] widget, represent /// overflow effects using the [TextOverflow] enum. The /// [TextOverflow.ellipsis] value corresponds to setting this property to /// U+2026 HORIZONTAL ELLIPSIS (…). String? get ellipsis => _ellipsis; String? _ellipsis;

关键在于第二段文档注释,当maxLines为null且文本不止一行(文本宽度超出一行的宽度)时,ellipsis会用于第一行,也就是截断为一行。文档已经说的很清楚了,我也不知道当时为啥还要标个TODO😂。不过有句话说得好,来都来了,怎么也得继续再往下看看具体是在哪做文本截断处理的。

找了一番,发现难以继续分析下去,文本的绘制应该涉及到了Flutter引擎部分,光靠Flutter框架代码想要分析下去有点难。不过,也不是没有收获,在RenderParagraph类的performLayout方法中发现了一些关键调用:

screenshot5

执行布局时,调用textPainter.size获取的高度只有一行的高度,最关键的是调用_textPainter.didExceedMaxLines返回了true,这表示文本被截断或被省略。 didExceedMaxLines方法的定义:

/// Whether any text was truncated or ellipsized. /// /// If [maxLines] is not null, this is true if there were more lines to be /// drawn than the given [maxLines], and thus at least one line was omitted in /// the output; otherwise it is false. /// /// If [maxLines] is null, this is true if [ellipsis] is not the empty string /// and there was a line that overflowed the `maxWidth` argument passed to /// [layout]; otherwise it is false. /// /// Valid only after [layout] has been called. bool get didExceedMaxLines { assert(_debugAssertTextLayoutIsValid); return _paragraph!.didExceedMaxLines; }

didExceedMaxLines方法的第三段文档注释也明确说了,如果maxLines为null且ellipsis不为空,会返回true。沿着_paragraph!.didExceedMaxLines来到了Flutter SDK目录/bin/cache/pkg/sky_engine/lib/ui/text.dart文件:

/// True if there is more vertical content, but the text was truncated, either /// because we reached `maxLines` lines of text or because the `maxLines` was /// null, `ellipsis` was not null, and one of the lines exceeded the width /// constraint. /// /// See the discussion of the `maxLines` and `ellipsis` arguments at /// [ParagraphStyle.new]. @Native(symbol: 'Paragraph::didExceedMaxLines', isLeaf: true) external bool get didExceedMaxLines;

从这开始,已经脱离Flutter框架,来到Flutter引擎。经过一番查找,最终锁定在了skia项目中的TextWrapper.cpp文件,里面有个breakTextIntoLines函数,顾名思义,这个函数的作用就是将文本分成几行。这个函数有点长,又是C++写的,直接看有点累,如果能调试会轻松很多,调试环境的搭建可以参考这篇文章Flutter - 搭建引擎调试环境(iOS)。

screenshot6

从调试结果看,如果有ellipsis且不限制行数时,会提前终止循环,也就是第一行就被截断。分析到这,基本可以确定文本最终就是在这被分行截断。

解决方案

Text组件设置不限行数时,请勿将overflow属性设置为TextOverflow.ellipsis。

最后

如果这篇文章对你有所帮助,请不要吝啬你的点赞👍加星🌟,谢谢~



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有